package com.xiaozhi.dialogue.llm;

import cn.hutool.core.collection.CollectionUtil;
import com.xiaozhi.communication.common.ChatSession;
import com.xiaozhi.dialogue.llm.api.StreamResponseListener;
import com.xiaozhi.dialogue.llm.factory.ChatModelFactory;
import com.xiaozhi.dialogue.llm.memory.ChatMemory;
import com.xiaozhi.entity.*;
import com.xiaozhi.service.IKnowledgeDocumentService;
import com.xiaozhi.service.SysRoleService;
import com.xiaozhi.service.VectorSearchService;
import com.xiaozhi.utils.EmojiUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * 负责管理和协调LLM相关功能
 * TODO 重构：改成Domain Entity: ChatRole(聊天角色)，管理对话历史记录，管理对话工具调用等。
 */
@Service
public class ChatService {
    private static final Logger logger = LoggerFactory.getLogger(ChatService.class);

    public static final String TOOL_CONTEXT_SESSION_KEY = "session";

    // 句子结束标点符号模式（中英文句号、感叹号、问号）
    private static final Pattern SENTENCE_END_PATTERN = Pattern.compile("[。！？!?]");

    // 逗号、分号等停顿标点
    private static final Pattern PAUSE_PATTERN = Pattern.compile("[，、；,;]");

    // 冒号和引号等特殊标点
    private static final Pattern SPECIAL_PATTERN = Pattern.compile("[：:\"]");

    // 换行符
    private static final Pattern NEWLINE_PATTERN = Pattern.compile("[\n\r]");

    // 数字模式（用于检测小数点是否在数字中）
    private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+\\.\\d+");

    // 表情符号模式
    private static final Pattern EMOJI_PATTERN = Pattern.compile("\\p{So}|\\p{Sk}|\\p{Sm}");


    // 增强版URL正则：匹配http/https开头的完整URL（包含路径、参数、锚点）
    private static final Pattern URL_PATTERN = Pattern.compile("https?://[\\w\\-\\.]+(:\\d+)?(/[\\w\\-./?%&=]*)?");
    // 最小句子长度（字符数）
    private static final int MIN_SENTENCE_LENGTH = 5;

    // 新句子判断的字符阈值
    private static final int NEW_SENTENCE_TOKEN_THRESHOLD = 8;

    @Resource
    private ChatMemory chatMemoryStore;

    // TODO 移到构建者模式，由连接通过认证，可正常对话时，创建实例，构建好一个完整的Role.
    @Resource
    private ChatModelFactory chatModelFactory;

    /**
     * 处理用户查询（同步方式）
     *
     * @param session         会话信息
     * @param message         用户消息
     * @param useFunctionCall 是否使用函数调用
     * @return 模型回复
     */
    public String chat(ChatSession session, String message, boolean useFunctionCall) {
        try {
            long startTime = System.currentTimeMillis();
            // 获取ChatModel
            ChatModel chatModel = chatModelFactory.takeChatModel(session);

            ChatOptions chatOptions = ToolCallingChatOptions.builder()
                    .toolCallbacks(useFunctionCall ? session.getToolCallbacks() : new ArrayList<>())
                    .toolContext(TOOL_CONTEXT_SESSION_KEY, session)
                    .build();

            UserMessage userMessage = new UserMessage(message);
            List<Message> messages = session.getConversation().prompt(userMessage);
            Prompt prompt = new Prompt(messages,chatOptions);

            ChatResponse chatResponse = chatModel.call(prompt);
            long endTime = System.currentTimeMillis();
            logger.info("同步模型调用完成，耗时: {}ms", endTime - startTime);
            if (chatResponse == null || chatResponse.getResult().getOutput().getText() == null) {
                logger.warn("模型响应为空或无生成内容");
                return "抱歉，我在处理您的请求时遇到了问题。请稍后再试。";
            }
            AssistantMessage assistantMessage =chatResponse.getResult().getOutput();

            Thread.startVirtualThread(() -> {// 异步持久化
                // 保存AI消息，会被持久化至数据库。
                session.getConversation().addMessage(userMessage,session.getUserTimeMillis(),assistantMessage,session.getAssistantTimeMillis());
            });
            return assistantMessage.getText();

        } catch (Exception e) {
            logger.error("处理查询时出错: {}", e.getMessage(), e);
            return "抱歉，我在处理您的请求时遇到了问题。请稍后再试。";
        }
    }
    @Resource
    private SysRoleService roleService;

    @Autowired
   private VectorSearchService VectorSearchService;
    @Autowired
    private IKnowledgeDocumentService documentService;
    /**
     * 处理用户查询（流式方式）
     *
     * @param message         用户消息
     * @param useFunctionCall 是否使用函数调用
     */
    public Flux<ChatResponse> chatStream(ChatSession session, String message,
            boolean useFunctionCall) {
        return chatStream(session, message, useFunctionCall, null);
    }

    /**
     * 处理用户查询（流式方式）
     *
     * @param message         用户消息
     * @param useFunctionCall 是否使用函数调用
     * @param searchResults   预计算的搜索结果，如果为null则内部计算
     */
    public Flux<ChatResponse> chatStream(ChatSession session, String message,
            boolean useFunctionCall, List<SearchResult> searchResults) {
        // 构造包含检索结果的增强消息
        String enhancedMessage = buildEnhancedMessage(message, searchResults);
        System.out.println("构造包含检索结果的增强消息enhancedMessage:"+enhancedMessage);
        // 记录模型调用开始时间
        long modelCallStartTime = System.currentTimeMillis();
        // 获取ChatModel
        ChatModel chatModel = chatModelFactory.takeChatModel(session);

        ChatOptions chatOptions = ToolCallingChatOptions.builder()
                .toolCallbacks(useFunctionCall ? session.getToolCallbacks() : new ArrayList<>())
                .toolContext(TOOL_CONTEXT_SESSION_KEY, session)
                .build();

        UserMessage userMessage = new UserMessage(enhancedMessage);
        List<Message> messages = session.getConversation().prompt(userMessage);
        Prompt prompt = new Prompt(messages, chatOptions);
        // 记录模型调用准备完成时间
        long modelPrepEndTime = System.currentTimeMillis();
        logger.info("模型调用准备耗时: {}ms", modelPrepEndTime - modelCallStartTime);
        // 调用实际的流式聊天方法
        logger.info("开始调用模型流式接口");
        long streamCallStartTime = System.currentTimeMillis();
        Flux<ChatResponse> responseFlux = chatModel.stream(prompt);
        long streamCallEndTime = System.currentTimeMillis();
        logger.info("模型流式接口调用耗时: {}ms", streamCallEndTime - streamCallStartTime);
        return responseFlux;
    }


    public    List<String>  getDocumentList(ChatSession session){
        List<String> documentIds =new ArrayList<>();
        SysDevice device = session.getSysDevice();
        System.out.println("当前设备信息888888888888:"+device);
        SysRole role = roleService.selectRoleById(device.getRoleId());
        System.out.println("当前角色（（（（（（（（（（（role:"+role);
        // 绑定的数据集
        String documentType = role.getDocumentType();
        if(StringUtils.isNotBlank(documentType)){
            String[] document = documentType.split(",");
            KnowledgeDocument knowledgeDocument = new KnowledgeDocument();
            knowledgeDocument.setIdTypeList(Arrays.asList( document));
            List<KnowledgeDocument> knowledgeDocuments = documentService.selectKnowledgeDocumentList(knowledgeDocument);
            System.out.println("当前角色绑定的数据集:"+knowledgeDocuments);
             documentIds = knowledgeDocuments.stream().map(KnowledgeDocument::getId).toList();
        }
        return documentIds;
    }

    private String buildEnhancedMessage(String question, List<SearchResult> searchResults) {
        if (searchResults == null || searchResults.isEmpty()) {
            return question;
        }

//        StringBuilder enhancedMessage = new StringBuilder();
//        enhancedMessage.append("请简洁回答问题，不要展开过多背景信息。如果有图片或者视频链接，请直接给我返回图片或视频的URL地址，不要返回Markdown格式形式的图片或视频链接。" +
//                "不要这种格式的图片![](http://aisheart-test.oss-cn-beijing.aliyuncs.com/1992874738973179905/2065425421049136245/7bddd9f8d9a34fddbbfde9eaae9eecf8_20251126tmp_f0d9dfc6da4d4c9dbceaf1ae9f8f8e99.png),直接返回括号内的路径\n\n\n");
//        for (int i = 0; i < searchResults.size(); i++) {
//            SearchResult result = searchResults.get(i);
//            enhancedMessage.append("信息 ").append(i + 1).append(":\n")
//                    .append(result.getContent()) // 假设SearchResult有getContent()方法
//                    .append("\n\n");
//        }
//        enhancedMessage.append("用户问题：").append(originalMessage);

        //2025 11 29

        String searchResultext="";
       for (int i = 0; i < searchResults.size(); i++) {
            SearchResult result = searchResults.get(i);
            searchResultext+=result.getContent();
        }

        String prompt;
        if (searchResults != null && !searchResults.isEmpty()) {
             prompt="以下是信息内容：\n"+searchResultext+"\n请基于上述信息回答用户问题,如果有图片或者视频链接，请直接给我返回图片或视频的URL地址，不要返回Markdown格式形式的图片或视频链接。用户问题："+question;
        } else {
            prompt = question; // 直接使用原问题，不添加前缀
        }
        return prompt;
    }


    public void chatStreamBySentence(ChatSession session, String message, boolean useFunctionCall,
            TriConsumer<String, Boolean, Boolean> sentenceHandler) {
        try {
            //记录这个方法的耗时
            long startTime = System.currentTimeMillis();
            List<String> documentList = this.getDocumentList(session);
            List<SearchResult> search = VectorSearchService.searchWithRerank(message, documentList,2);
            logger.info( "向量数据中检索的内容===="+search);
            // 记录答中状态
            String dzStatus = "0";
            if(CollectionUtil.isNotEmpty( search)){
                dzStatus="1";
            }
            // 创建流式响应监听器
            StreamResponseListener streamListener = new TokenStreamResponseListener(session, message, sentenceHandler, dzStatus);
            final StringBuilder toolName = new StringBuilder(); // 当前句子的缓冲区
            // 调用现有的流式方法
//            Flux<ChatResponse> responseFlux = chatStream(session, message, useFunctionCall, search);  // 传递预计算的搜索结果
            logger.info("开始订阅模型响应流");
            long subscribeStartTime = System.currentTimeMillis();
            // 调用现有的流式方法
            chatStream(session, message, useFunctionCall, search).subscribe(
                    chatResponse -> {
                        long endTime = System.currentTimeMillis();
                        logger.info("从订阅到接收到第一个响应耗时: {}ms", endTime - subscribeStartTime);
                        String token = chatResponse.getResult() == null
                                || chatResponse.getResult().getOutput() == null
                                || chatResponse.getResult().getOutput().getText() == null ? ""
                                        : chatResponse.getResult().getOutput().getText();
                        if (!token.isEmpty()) {
                            streamListener.onToken(token);
                        }
                        if (toolName.isEmpty() && useFunctionCall) {
                            Generation generation = chatResponse.getResult();
                            // 注意，不能用chatResponse.hasToolCalls()判断，当前chatResponse工具调用结果的返回，
                            // 是个文本类助手消息，hasToolCalls标识是false。必须溯源取meta
                            if (generation != null) {
                                ChatGenerationMetadata chatGenerationMetadata = generation.getMetadata();
                                String name = chatGenerationMetadata.get("toolName");
                                if (name != null && !name.isEmpty()) {
                                    toolName.append(name);
                                }
                            }
                        }
                    },
                    streamListener::onError,
                    () -> {
                        long completeTime = System.currentTimeMillis();
                        logger.info("LLM流式响应完成，总耗时: {}ms", completeTime - startTime);
                        streamListener.onComplete(toolName.toString());
                    });

        } catch (Exception e) {
            logger.error("处理LLM时出错，已耗时: {}ms, 错误: {}", e.getMessage(), e);
            // 发送错误信号
            sentenceHandler.accept("抱歉，我在处理您的请求时遇到了问题。", true, true);
        }
    }


    /**
     * 清除设备缓存
     *
     * @param deviceId 设备ID
     */
    public void clearMessageCache(String deviceId) {
        chatMemoryStore.clearMessages(deviceId);
    }

    /**
     * 判断文本是否包含实质性内容（不仅仅是空白字符或标点符号）
     *
     * @param text 要检查的文本
     * @return 是否包含实质性内容
     */
    private boolean containsSubstantialContent(String text) {
        if (text == null || text.trim().length() < MIN_SENTENCE_LENGTH) {
            return false;
        }

        // 移除所有标点符号和空白字符后，检查是否还有内容
        String stripped = text.replaceAll("[\\p{P}\\s]", "");
        return stripped.length() >= 2; // 至少有两个非标点非空白字符
    }

    /**
     * 三参数消费者接口
     */
    public interface TriConsumer<T, U, V> {
        void accept(T t, U u, V v);
    }

    class TokenStreamResponseListener implements StreamResponseListener {

        final StringBuilder currentSentence = new StringBuilder(); // 当前句子的缓冲区
        final StringBuilder contextBuffer = new StringBuilder(); // 上下文缓冲区，用于检测数字中的小数点
        final AtomicInteger sentenceCount = new AtomicInteger(0); // 已发送句子的计数
        final StringBuilder fullResponse = new StringBuilder(); // 完整响应的缓冲区
        final AtomicBoolean finalSentenceSent = new AtomicBoolean(false); // 跟踪最后一个句子是否已发送
        String message;// 用户消息内容
        ChatSession session;
        TriConsumer<String, Boolean, Boolean> sentenceHandler;
        String dzStatus; // 答中状态

        public TokenStreamResponseListener(ChatSession session, String message,
                TriConsumer<String, Boolean, Boolean> sentenceHandler, String dzStatus) {
            this.message = message;
            this.session = session;
            this.sentenceHandler = sentenceHandler;
            this.dzStatus = dzStatus;
        }

        @Override
        public void onToken(String token) {
            if (token == null || token.isEmpty()) {
                return;
            }
            // 将token添加到完整响应
            fullResponse.append(token);

            // 逐字符处理token（基于Unicode码点，支持表情符号等增补字符）
            for (int i = 0; i < token.length(); ) {
                int codePoint = token.codePointAt(i);
                String charStr = new String(Character.toChars(codePoint));

                // 将字符添加到上下文缓冲区（保留最近20个字符，用于检测数字、URL等上下文）
                contextBuffer.append(charStr);
                if (contextBuffer.length() > 20) {
                    contextBuffer.delete(0, contextBuffer.length() - 20);
                }

                // 将字符添加到当前句子缓冲区
                currentSentence.append(charStr);

                // 检查各种断句标记
                boolean isEndMark = SENTENCE_END_PATTERN.matcher(charStr).find();
                boolean isPauseMark = PAUSE_PATTERN.matcher(charStr).find();
                boolean isSpecialMark = SPECIAL_PATTERN.matcher(charStr).find();
                boolean isNewline = NEWLINE_PATTERN.matcher(charStr).find();
                boolean isEmoji = EmojiUtils.isEmoji(codePoint);

                // 检查当前句子是否包含颜文字（至少3个字符才可能是颜文字）
                boolean containsKaomoji = currentSentence.length() >= 3
                        ? EmojiUtils.containsKaomoji(currentSentence.toString())
                        : false;

                // 特殊处理：句号是否为数字中的小数点（如"0.271"）
                if (isEndMark && charStr.equals(".")) {
                    String context = contextBuffer.toString();
                    Matcher numberMatcher = NUMBER_PATTERN.matcher(context);
                    // 若上下文末尾3个字符内存在数字小数点模式，则不视为断句
                    if (numberMatcher.find() && numberMatcher.end() >= context.length() - 3) {
                        isEndMark = false;
                    }
                }

                String currentText = currentSentence.toString();
                boolean shouldSendSentence = false;

                // 核心优化1：URL完整保留逻辑
                Matcher urlMatcher = URL_PATTERN.matcher(currentText);
                boolean hasIncompleteUrl = false;
                if (urlMatcher.find()) {
                    // 若URL匹配但未到字符串末尾，说明URL未完整接收（避免截断）
                    hasIncompleteUrl = urlMatcher.end() < currentText.length();
                }

                // 核心优化2：Markdown图片链接完整保留（![描述](url)）
                boolean hasUnclosedImageLink = currentText.contains("![") && !currentText.contains(")");
                // 补充：Markdown普通链接（[描述](url)）也需要完整保留
                boolean hasUnclosedLink = currentText.contains("[") && currentText.contains("(") && !currentText.contains(")");

                // 特殊场景：避免在URL或链接中间断句
                if (charStr.equals(":") && contextBuffer.toString().endsWith("http:")) {
                    shouldSendSentence = false; // http: 后面不断句
                } else if (charStr.equals("/") && (currentText.contains("http://") || currentText.contains("https://"))) {
                    shouldSendSentence = false; // URL路径中不断句
                } else if (charStr.equals(")") && currentText.contains("![") && currentText.endsWith(")")) {
                    shouldSendSentence = false; // 图片链接结尾括号不断句
                } else if (charStr.equals("]") && currentText.contains("![") && currentText.endsWith("]")) {
                    shouldSendSentence = false; // 图片链接方括号不断句
                } else {
                    // 基础断句逻辑（叠加URL/链接完整性检测）
                    if (isEndMark || isNewline) {
                        // 强断句信号：句子结束标点、换行符
                        shouldSendSentence = true;
                    } else if ((isPauseMark || isSpecialMark || isEmoji || containsKaomoji)
                            && currentSentence.length() >= MIN_SENTENCE_LENGTH) {
                        // 弱断句信号：停顿标点、表情、颜文字（需句子长度达标）
                        shouldSendSentence = true;
                    }
                }

                // 最终断句条件：满足断句信号 + 句子长度达标 + 无未完成URL/链接
                if (shouldSendSentence
                        && currentSentence.length() >= MIN_SENTENCE_LENGTH
                        && !hasIncompleteUrl
                        && !hasUnclosedImageLink
                        && !hasUnclosedLink) {

                    String sentence = currentSentence.toString().trim();
                    // 过滤颜文字（可根据需求选择是否保留）
                    sentence = EmojiUtils.filterKaomoji(sentence);

                    // 过滤无实质内容的句子（如纯标点、纯表情）
                    if (containsSubstantialContent(sentence)) {
                        boolean isFirst = sentenceCount.get() == 0;
                        boolean isLast = false; // 最后一个句子在onComplete中标记

                        sentenceHandler.accept(sentence, isFirst, isLast);
                        sentenceCount.incrementAndGet();

                        // 清空当前句子缓冲区
                        currentSentence.setLength(0);
                    }
                }

                // 移动到下一个码点
                i += Character.charCount(codePoint);
            }
        }

        @Override
        public void onComplete(String toolName) {
            // 检查该会话是否已完成处理
            // 处理当前缓冲区剩余的内容（如果有）
            if (currentSentence.length() > 0 && containsSubstantialContent(currentSentence.toString())
                    && !finalSentenceSent.get()) {
                String sentence = currentSentence.toString().trim();
                boolean isFirst = sentenceCount.get() == 0;
                boolean isLast = true; // 这是最后一个句子

                sentenceHandler.accept(sentence, isFirst, isLast);
                sentenceCount.incrementAndGet();
                finalSentenceSent.set(true);
            } else if (!finalSentenceSent.get()) {
                // 如果没有剩余内容但也没有发送过最后一个句子，发送一个空的最后句子标记
                // 这确保即使没有剩余内容，也会发送最后一个句子标记
                boolean isFirst = sentenceCount.get() == 0;
                sentenceHandler.accept("", isFirst, true);
                finalSentenceSent.set(true);
            }

            persistMessages(toolName);

            // 记录处理的句子数量
            logger.debug("总共处理了 {} 个句子", sentenceCount.get());
        }

        /**
         * 保存消息,只保存用户输入与输出。
         * Message在没有持久化前，是不会有messageId的。
         * 是否需要把content为空和角色为tool的入库?
         * 目前不入库（这类主要是function_call的二次调用llm进行总结时的过程消息）
         * 具体的细节逻辑，由Conversation处理，ChatService不再负责消息持久化的职能。
         */
        void persistMessages(String toolName) {
            UserMessage userMessage = new UserMessage(message);
            Long userTimeMillis = session.getUserTimeMillis();
            AssistantMessage assistantMessage = new AssistantMessage(fullResponse.toString(), Map.of("toolName", toolName));
            Long assistantTimeMillis = session.getAssistantTimeMillis();
            session.getConversation().addMessage(userMessage, userTimeMillis, assistantMessage, assistantTimeMillis, dzStatus);
        }

        @Override
        public void onError(Throwable e) {
            logger.error("流式响应出错: {}", e.getMessage(), e);
            // 发送错误信号
            sentenceHandler.accept("抱歉，我在处理您的请求时遇到了问题。", true, true);

        }
    };

}
